package com.ophiux.lock.redis.redisLock;

import com.ophiux.lock.redis.enums.GlobalConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @desc: 基于Redis setnx实现分布式锁
 * @author: hhl
 * @date：2021/11/3 9:17
 */
@Service
public class RedisLockImpl implements RedisLock {
    /**
     * 默认超时时间毫秒
     */
    private static final Long DEFAULT_TIME_OUT = 300000L;
    /**
     * 默认sleep时间,100毫秒
     */
    private static final Long DEFAULT_SLEEP_TIME = 100L;

    private static final String LOCK_KEY = "hhl";

    /**
     * 缓存redis锁
     */
    private Map<Thread, RedisInfo> lockCacheMap = new ConcurrentHashMap<Thread, RedisInfo>();

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean tryLock() throws Exception {
        RedisInfo redisInfo = lockCacheMap.get(Thread.currentThread());
        if (redisInfo != null && redisInfo.isState()) {
            System.out.println("您在之前已经获取过锁，直接可重入");
            return true;
        }

        Long expireTime = System.currentTimeMillis() + DEFAULT_TIME_OUT;
        while (true) {
            //SET命令返回OK ，则证明获取锁成功
            Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(GlobalConstant.REDIS_CONNECTION_PREFIX + LOCK_KEY, "1", 60, TimeUnit.SECONDS);
            if (lock) {
                System.out.println("获取锁成功");
                lockCacheMap.put(Thread.currentThread(), new RedisInfo(true));
                return true;
            }else if(expireTime<System.currentTimeMillis()){
                //在timeout时间内仍未获取到锁，则获取失败
                System.out.println("重试时间已过，不能继续重试");
                return false;
            }
            //循环等待,睡眠100毫秒，减轻redis压力
            Thread.sleep(DEFAULT_SLEEP_TIME);
        }
    }

    @Override
    public boolean releaseLock() {
        //由创建线程来释放锁
        RedisInfo redisInfo = lockCacheMap.get(Thread.currentThread());
        if(redisInfo==null){
            return false;
        }
        return stringRedisTemplate.delete(GlobalConstant.REDIS_CONNECTION_PREFIX + LOCK_KEY);
    }
}
